package aip.assignment2.service;

import aip.assignment2.persistence.AddressDao;
import aip.assignment2.persistence.CountryDao;
import aip.assignment2.persistence.CreditCardDao;
import aip.assignment2.persistence.CreditCardTypeDao;
import aip.assignment2.persistence.CustomerDao;
import aip.assignment2.persistence.PersonNameDao;
import aip.assignment2.persistence.PersonTitleDao;
import aip.assignment2.persistence.ProductDao;
import aip.assignment2.persistence.PurchaseDao;
import aip.assignment2.persistence.PurchaseProductDao;
import aip.assignment2.persistence.SqlParameter;
import aip.assignment2.persistence.entity.Country;
import aip.assignment2.persistence.entity.CreditCardType;
import aip.assignment2.persistence.entity.Customer;
import aip.assignment2.persistence.entity.PersonTitle;
import aip.assignment2.persistence.entity.Product;
import aip.assignment2.persistence.entity.Purchase;
import aip.assignment2.persistence.entity.PurchaseProduct;
import aip.assignment2.service.entity.ProductInCart;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

/**
 * This class manages purchases.
 * It has to be made Singleton because there are product updates concerning with stock.
 */
@Singleton(name = "aip.assignment2.service.PurchaseManager")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class PurchaseManager implements Serializable
{
	@EJB
	private Application m_Application;
	@EJB
	private ProductDao m_ProductDao;
	@EJB
	private PurchaseDao m_PurchaseDao;
	@EJB
	private CustomerDao m_CustomerDao;
	@EJB
	private PersonNameDao m_PersonNameDao;
	@EJB
	private PersonTitleDao m_PersonTitleDao;
	@EJB
	private AddressDao m_AddressDao;
	@EJB
	private CountryDao m_CountryDao;
	@EJB
	private CreditCardDao m_CreditCardDao;
	@EJB
	private CreditCardTypeDao m_CreditCardTypeDao;
	@EJB
	private PurchaseProductDao m_PurchaseProductDao;
	/**
	 * This method creates a new purchase given its products and its customer.
	 * @param productsInCart the products in cart added
	 * @param customer the customer info
	 * @return the result Purchase
	 */
	public synchronized Purchase create(List<ProductInCart> productsInCart, Customer customer)
	{
		m_Application.getLogger().info("Now starting to create a new purchase.");
		if (productsInCart == null || productsInCart.isEmpty() || customer == null)
		{
			m_Application.getLogger().severe("The input parameters are invalid.");
			m_Application.getLogger().info("Purchase creation fails.");
			return null;
		}
		// Insert the new customer
		m_Application.getLogger().fine("Creating Customer ...");
		m_AddressDao.insert(customer.getAddress());
		m_PersonNameDao.insert(customer.getName());
		m_CreditCardDao.insert(customer.getCreditCard());
		m_CustomerDao.insert(customer);
		// Create new purchase
		m_Application.getLogger().fine("Creating Purchase ...");
		Purchase result = new Purchase();
		result.setCustomer(customer);
		result.setStatus("ORDERED");
		List<Purchase> purchases = m_PurchaseDao.selectByNamedQuery(0, 1, "Purchase.findAllOrderByNumber");
		String resultNumber = "AIP2-0000";
		if (!purchases.isEmpty())
		{
			String maxNumber = purchases.get(0).getNumber();
			int newNumber = Integer.parseInt(maxNumber.substring(5)) + 1;
			resultNumber = String.format("AIP2-%04d", newNumber);
		}
		result.setNumber(resultNumber);
		result.setCreatedOn(new Date());
		m_PurchaseDao.insert(result);
		// Create new PurchaseProducts
		m_Application.getLogger().fine("Creating PurchaseProducts ...");
		for (int i = 0; i < productsInCart.size(); i++)
		{
			ProductInCart productInCart = productsInCart.get(i);
			Product product = productsInCart.get(i).getProduct();
			PurchaseProduct purchaseProduct = new PurchaseProduct();
			// Verify stock
			if (product.getStock() < productInCart.getQuantity())
			{
				return null;
			}
			product.setStock(product.getStock() - productInCart.getQuantity());
			m_ProductDao.update(product);
			purchaseProduct.setProduct(productsInCart.get(i).getProduct());
			purchaseProduct.setQuantity(productsInCart.get(i).getQuantity());
			purchaseProduct.setPurchase(result);
			m_PurchaseProductDao.insert(purchaseProduct);
		}
		m_Application.getLogger().info(String.format("Purchase creation succeeds with number \"%s\".", result.getNumber()));
		//m_Application.getCurrentSession().invalidate();
		return result;
	}
	/**
	 * This method gets the person title by its ID.
	 * @param id the person title ID
	 * @return the result PersonTitle
	 */
	public PersonTitle getPersonTitleById(int id)
	{
		return m_PersonTitleDao.selectById(id);
	}
	/**
	 * This method gets all the person titles.
	 * @return the result list of PersonTitle
	 */
	public List<PersonTitle> getPersonTitles()
	{
		return m_PersonTitleDao.select();
	}
	/**
	 * This method gets the country by its ID.
	 * @param id the country ID
	 * @return the result Country
	 */
	public Country getCountryById(int id)
	{
		return m_CountryDao.selectById(id);
	}
	/**
	 * This method gets all countries.
	 * @return the result list of Country
	 */
	public List<Country> getCountries()
	{
		return m_CountryDao.select();
	}
	/**
	 * This method gets the credit card type by its ID.
	 * @param id the credit card type ID
	 * @return the result CreditCardType
	 */
	public CreditCardType getCreditCardTypeById(int id)
	{
		return m_CreditCardTypeDao.selectById(id);
	}
	/**
	 * This method gets all credit card types.
	 * @return the result list of CreditCardType
	 */
	public List<CreditCardType> getCreditCardTypes()
	{
		return m_CreditCardTypeDao.select();
	}
	/**
	 * This method gets the purchase by its number.
	 * @param number the purchase number
	 * @return the result Purchase
	 */
	public Purchase getPurchaseByNumber(String number)
	{
		if (number == null)
		{
			return null;
		}
		return m_PurchaseDao.selectSingleResultByNamedQuery("Purchase.findByNumber", new SqlParameter("number", number));
	}
	/**
	 * This method gets the purchase by its customer's surname
	 * @param surname the customer surname
	 * @return the result Purchase
	 */
	public Purchase getPurchaseBySurname(String surname)
	{
		if (surname == null)
		{
			return null;
		}
		return m_PurchaseDao.selectSingleResultByNamedQuery("Purchase.findBySurname", new SqlParameter("surname", surname));
	}
	/**
	 * This method gets the purchase by its number and its customer's surname
	 * @param surname the customer surname
	 * @return the result Purchase
	 */
	public Purchase getPurchaseByNumberAndSurname(String number, String surname)
	{
		if (number == null || surname == null)
		{
			return null;
		}
		return m_PurchaseDao.selectSingleResultByNamedQuery("Purchase.findByNumberAndSurname", new SqlParameter("number", number), new SqlParameter("surname", surname));
	}
	/**
	 * This method gets all paid purchases.
	 * @return the result list of Purchase
	 */
	public List<Purchase> getPaidPurchases()
	{
		return m_PurchaseDao.selectByNamedQuery("Purchase.findByStatus", new SqlParameter("status", "PAID"));
	}
	/**
	 * This method gets all ordered or paid purchases.
	 * @return the result list of Purchase
	 */
	public List<Purchase> getOrderedOrPaidPurchases()
	{
		return m_PurchaseDao.selectByNamedQuery("Purchase.findOrderedOrPaidPurchases");
	}
	/**
	 * This method gets all ordered or paid purchases by number.
	 * @return the result list of Purchase
	 */
	public List<Purchase> getOrderedOrPaidPurchases(String number)
	{
		if (number == null)
		{
			return null;
		}
		List<Purchase> purchases = m_PurchaseDao.selectByNamedQuery("Purchase.findOrderedOrPaidPurchasesByNumber", new SqlParameter("number", "%" + number + "%"));
		return purchases;
	}
	/**
	 * This method updates an existing purchase.
	 * @param purchase the purchase to be updated
	 */
	public void update(Purchase purchase)
	{
		m_PurchaseDao.update(purchase);
	}
}
